package cn.zwx.activiti.example.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.zwx.activiti.example.common.enums.ErrorEnum;
import cn.zwx.activiti.example.common.enums.FormDataIsParamEnum;
import cn.zwx.activiti.example.common.enums.FormDataParamType;
import cn.zwx.activiti.example.common.enums.TaskTypeEnum;
import cn.zwx.activiti.example.common.exception.BizException;
import cn.zwx.activiti.example.manger.ProcessImageManager;
import cn.zwx.activiti.example.mapper.FormDataMapper;
import cn.zwx.activiti.example.model.bo.FormDataBO;
import cn.zwx.activiti.example.model.po.FormData;
import cn.zwx.activiti.example.model.req.CompleteTaskReq;
import cn.zwx.activiti.example.model.req.FormDataShowReq;
import cn.zwx.activiti.example.model.req.UserTaskImageReq;
import cn.zwx.activiti.example.model.req.UserTaskReq;
import cn.zwx.activiti.example.model.resp.FormDataShowResp;
import cn.zwx.activiti.example.model.resp.UserTaskResp;
import cn.zwx.activiti.example.service.UserTaskService;
import cn.zwx.activiti.example.utils.FormDataUtil;
import org.activiti.api.process.model.ProcessInstance;
import org.activiti.api.process.runtime.ProcessRuntime;
import org.activiti.api.runtime.shared.query.Page;
import org.activiti.api.task.model.Task;
import org.activiti.api.task.model.builders.TaskPayloadBuilder;
import org.activiti.api.task.runtime.TaskRuntime;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.TaskQuery;
import org.activiti.runtime.api.query.impl.PageImpl;
import org.apache.commons.lang3.BooleanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @description:
 * @projectName:activiti-example
 * @see:cn.zwx.activiti.example.service.impl
 * @author:zhangwenxue
 * @createTime:2021/6/2 21:13
 * @version:1.0
 */
@Service
public class UserTaskServiceImpl implements UserTaskService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private TaskService taskService;

    @Autowired
    private ProcessRuntime processRuntime;

    @Autowired
    private ProcessImageManager processImageManager;

    @Autowired
    private TaskRuntime taskRuntime;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private FormDataMapper formDataMapper;


    @Override
    public Page<UserTaskResp> getTasks(UserTaskReq req) {
        // 构建查询参数
        TaskQuery taskQuery = taskService.createTaskQuery();
        if (StrUtil.isNotBlank(req.getProcessDefinitionKey())){
            taskQuery.processDefinitionKey(req.getProcessDefinitionKey());
        }
        if (StrUtil.isNotBlank(req.getProcessInstanceId())){
            taskQuery.processInstanceId(req.getProcessInstanceId());
        }
        if (StrUtil.isNotBlank(req.getProcessDefinitionName())){
            taskQuery.processDefinitionNameLike(req.getProcessDefinitionName());
        }
        // 查询总共条数
        Long count = taskQuery.taskCandidateOrAssigned(req.getUserName(), Arrays.asList(req.getUserName())).count();
        // 查询数据
        List<org.activiti.engine.task.Task> list = taskQuery.taskCandidateOrAssigned(req.getUserName(), Arrays.asList(req.getUserName()))
                .orderByTaskCreateTime().desc().listPage(req.getStartIndex(), req.getEndIndex());
        // 返回参数封装
        List<UserTaskResp>  respList = CollectionUtil.newArrayList();
        if (CollectionUtil.isEmpty(list)){
            return new PageImpl<>(respList,count.intValue());
        }
        // 返回参数组装
        for (org.activiti.engine.task.Task tk : list) {
            UserTaskResp resp = new UserTaskResp();
            resp.setId(tk.getId());
            resp.setCreatedDate(DateUtil.format(tk.getCreateTime(), DatePattern.NORM_DATETIME_PATTERN));
            resp.setDueDate(DateUtil.format(tk.getDueDate(), DatePattern.NORM_DATETIME_PATTERN));
            resp.setProcessDefinitionId(tk.getProcessDefinitionId());
            // 查询流程实例信息
            ProcessInstance processInstance = processRuntime.processInstance(tk.getProcessInstanceId());
            resp.setProcessDefinitionKey(processInstance.getProcessDefinitionKey());
            resp.setProcessInstanceId(tk.getProcessInstanceId());
            resp.setProcessInstanceName(tk.getName());
            resp.setTaskName(tk.getName());
            String assignee = tk.getAssignee();

            resp.setTaskType(StrUtil.isNotBlank(assignee)? TaskTypeEnum.ASSIGNEE_TASK.getCode():TaskTypeEnum.CANDIDATE_TASK.getCode());
            resp.setTaskTypeName(StrUtil.isNotBlank(assignee)? TaskTypeEnum.ASSIGNEE_TASK.getMsg():TaskTypeEnum.CANDIDATE_TASK.getMsg());
            respList.add(resp);
        }
        return new PageImpl<>(respList,count.intValue());
    }

    /**
     * @description 查询任务流程图
     * @return java.io.BufferedInputStream
     * @author zhangwenxue
     * @createTime 2021/6/3 12:28
     **/
    @Override
    public InputStream getTasksImage(UserTaskImageReq req) throws Exception {
        return processImageManager.getFlowImgByProcInstId(req.getProcessInstanceId());
    }

    /**
     * @description 完成任务
     * @return AjaxResponse
     * @author zhangwenxue
     * @createTime 2021/6/2 21:15
     **/
    @Override
    public void completeTask(CompleteTaskReq req) {
     // 1.查询任务信息
      Task task = taskRuntime.task(req.getTaskId());
      if (Objects.isNull(task)){
          logger.error("未查询到任务信息,任务id:{}",req.getTaskId());
          throw new BizException(ErrorEnum.NOT_FOUND_TASK_FAILED.getErrorCode(),ErrorEnum.NOT_FOUND_TASK_FAILED.getErrorMsg());
      }
      // 2.判断是否是拾取任务
      if (task.getAssignee() == null){
          // 此时是拾取任务 需要先进行拾取
          taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());
      }
      // 3.判断是否需要传入参数
      Boolean hasVariables = Boolean.FALSE;//没有任何参数
      HashMap<String, Object> variables = new HashMap<String, Object>();
      if (StrUtil.isNotBlank(req.getFormData())){
          List<FormDataBO> formDataBOList = FormDataUtil.parasFormDataForBO(req.getFormData(), task);
          List<FormData> formDataList = CollectionUtil.newArrayList();
          for (FormDataBO formDataBO : formDataBOList) {
              //构建参数集合
              if (formDataBO.getHasParam().equals(FormDataIsParamEnum.IS_PARAM.getCode())){
                  switch (formDataBO.getControlType()){
                      case "string":
                          variables.put(formDataBO.getVariableName(), formDataBO.getControlVaule());
                          hasVariables = Boolean.TRUE; break;
                      case "long":
                          variables.put(formDataBO.getVariableName(),Long.valueOf(formDataBO.getControlVaule()));
                          hasVariables = Boolean.TRUE; break;
                      case "boolean":
                          variables.put(formDataBO.getVariableName(),Boolean.valueOf(formDataBO.getControlVaule()));
                          hasVariables = Boolean.TRUE; break;
                      case "date":
                          variables.put(formDataBO.getVariableName(),DateUtil.parse(formDataBO.getControlVaule(),DatePattern.NORM_DATETIME_PATTERN));
                          hasVariables = Boolean.TRUE; break;
                      default:  logger.info("控件参数类型配置错误：" + formDataBO.getControlId() + "的参数类型不存在，" + formDataBO.getControlType());
                  }
              }else {
                  logger.info("控件值不作为控件ID：" + formDataBO.getControlId() + "参数值，" + formDataBO.getControlVaule());
              }
              // 转换保存的数据
              FormData formData = new FormData();
              BeanUtil.copyProperties(formDataBO,formData);
              formData.setIsParams(formDataBO.getHasParam());
              formData.setUpdateTime(new DateTime());
              formData.setCreateTime(new DateTime());
              formDataList.add(formData);
          }

          // 5.写入数据库
          int size = formDataMapper.batchInsert(formDataList);
          logger.info("写入数据库成功:{}条",size);
      }

        // 4.提交任务
        if (hasVariables) {
            //带参数完成任务
            taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId())
                    .withVariables(variables)
                    .build());
        } else {
            taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId())
                    .build());
        }


    }

    /**
     * @description 渲染动态表单
     * @return java.util.List<cn.zwx.activiti.example.model.resp.FormDataShowResp>
     * @author zhangwenxue
     * @createTime 2021/6/3 22:17
     **/
    @Override
    public List<FormDataShowResp> formDataShow(FormDataShowReq req) {
        // 1.查询任务信息
        org.activiti.api.task.model.Task task = taskRuntime.task(req.getTaskId());
        if (Objects.isNull(task)){
            logger.error("未查询到任务信息,任务id:{}",req.getTaskId());
            throw new BizException(ErrorEnum.NOT_FOUND_TASK_FAILED.getErrorCode(),ErrorEnum.NOT_FOUND_TASK_FAILED.getErrorMsg());
        }
        // 2.构建表单控件历史数据字典
        Map<String, String> historyMap = new HashMap<>();
        List<FormData> formDataList = formDataMapper.selectFormData(task.getProcessInstanceId());
        for (FormData formData : formDataList) {
            historyMap.put(formData.getControlId(), formData.getControlVaule());
        }

        // 3. 注意!!!!!!!!:表单Key必须要任务编号一模一样，因为参数需要任务key，但是无法获取，只能获取表单key“task.getFormKey()”当做任务key
        UserTask userTask = (UserTask) repositoryService.getBpmnModel(task.getProcessDefinitionId())
                .getFlowElement(task.getFormKey());
        if (userTask == null) {
            logger.info("未查询到表单相关的数据,请求参数:{}",task.getProcessDefinitionId());
            return CollectionUtil.newArrayList();
        }
        // 4.解析数据并返回
        return FormDataUtil.parasFormData(userTask.getFormProperties(), historyMap);
    }


}
